import java.net.*;
import java.util.Vector;
import java.io.*;

/*
  Java Transparent Proxy
  Copyright (C) 1999 by Didier Frick (http://www.dfr.ch/)
  This software is provided under the GNU general public license (http://www.gnu.org/copyleft/gpl.html).
 */

public class ProxyThread extends Thread {

	// Holds all the currently active StreamCopyThreads
	private Vector<StreamCopyThread> connections=new Vector<StreamCopyThread>();
	// Used to synchronize the connection-handling threads with this thread
	private Object lock=new Object();
	// The address to forward connections to
	private InetAddress dstAddr;
	// The port to forward connections to
	private int dstPort;
	// Backlog parameter used when creating the ServerSocket
	protected static final int backLog=100;
	// Timeout waiting for a StreamCopyThread to finish
	public static final int threadTimeout=2000; //ms
	// Linger time
	public static final int lingerTime=180; //seconds (?)
	// Size of receive buffer
	public static final int bufSize=1024;
	// Header to prepend to log messages
	private String header;
	// This proxy's server socket
	private ServerSocket srvSock;
	// Debug flag
	private boolean debug=true;

	private int countConnection;

	// Log streams for output and error messages
	private PrintStream out;
	private PrintStream err;
	private Logger log;


	private static final String 
	argsMessage="Arguments: ( [source_address] source_port dest_address dest_port ) | config_file";
	private static final String 
	propertyPrefix="proxy";


	public ProxyThread(String src, int srcPort, String dst, int dstPort,
			PrintStream out, PrintStream err) {
		this.out=out;
		this.err=err;

		InetAddress srcAddr;
		InetAddress dstAddr;

		try {
			srcAddr = (src==null) ? null : InetAddress.getByName(src);
			dstAddr = (dst==null) ? null : InetAddress.getByName(dst);
			this.srvSock=(srcAddr==null) ? new ServerSocket(
					srcPort,backLog) :  
						new ServerSocket(srcPort,backLog,srcAddr);
					this.dstAddr=dstAddr;
					this.dstPort=dstPort;
					this.header=(srcAddr==null ? "" : srcAddr.toString())+":"+srcPort+" <-> "+dstAddr+":"+dstPort;

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.countConnection = 0;
		this.log = new Logger("proxy-thread.log");
		//start();
	}

	public void run() {
		out.println(header+" : starting");
		try {
			while(!isInterrupted()) {
				Socket serverSocket=srvSock.accept();
				out.println(header+" : incomming connection");
				log.write(header+" : incomming connection");
				try {
					serverSocket.setSoLinger(true,lingerTime);
					Socket clientSocket=new Socket(dstAddr,dstPort);
					clientSocket.setSoLinger(true,lingerTime);

					StreamCopyThread sToC = new StreamCopyThread(serverSocket,clientSocket, bufSize, header, debug, lock, 0, countConnection);
					StreamCopyThread cToS = new StreamCopyThread(clientSocket,serverSocket, bufSize, header, debug, lock, 1, countConnection);

					sToC.setPeer(cToS);
					cToS.setPeer(sToC);
					synchronized(lock) {
						connections.addElement(cToS);
						connections.addElement(sToC);
						sToC.start();
						cToS.start();
					}
					countConnection++;
				} catch(Exception xc) {
					err.println(header+":"+xc);
					if(debug)
						xc.printStackTrace();
				}
			}
			srvSock.close();
		} catch(IOException xc) {
			err.println(header+":"+xc);
			if(debug)
				xc.printStackTrace();
		} finally {
			cleanup();
			out.println(header+" : stopped");
		}
	}

	public void stopProxy()
	{
		out.println(header+" : stoping ...");
		this.interrupt();
		try {
			srvSock.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void cleanup() {
		synchronized(lock) {
			try {
				while(connections.size()>0) {
					StreamCopyThread sct=(StreamCopyThread)connections.elementAt(0);
					sct.interrupt();
					sct.join(threadTimeout);
				}
			} catch(InterruptedException xc) {
			}
		}
	}

	public Vector<StreamCopyThread> getConnections()
	{
		return this.connections;
	}


	/*
	private static ProxyThread addProxy(String src,String srcPort, String dst, String dstPort,
			PrintStream out, PrintStream err) throws
			UnknownHostException, IOException
			{
		InetAddress srcAddr=(src==null) ? null : InetAddress.getByName(src);
		return new ProxyThread(srcAddr,Integer.parseInt(srcPort),
				InetAddress.getByName(dst),Integer.parseInt(dstPort),out,err);
			}
	 */
	/*
	private static Vector parseConfigFile(String fileName,PrintStream out,PrintStream err) throws 
	FileNotFoundException, IOException, UnknownHostException
	{
		Vector result=new Vector();
		FileInputStream in=new FileInputStream(fileName);
		Properties props= new Properties();
		props.load(in);
		in.close();
		for(int i=0;;i++) {
			String srcAddr=props.getProperty(propertyPrefix+"."+i+".sourceAddr");
			String srcPort=props.getProperty(propertyPrefix+"."+i+".sourcePort");
			if(srcPort==null)
				break;
			String dstAddr=props.getProperty(propertyPrefix+"."+i+".destAddr");
			String dstPort=props.getProperty(propertyPrefix+"."+i+".destPort");
			if(dstAddr==null) {
				throw new IllegalArgumentException("Missing destination address for proxy "+i);
			}
			if(dstPort==null) {
				throw new IllegalArgumentException("Missing destination port for proxy "+i);
			}
			result.addElement(addProxy(srcAddr,srcPort,dstAddr,dstPort,out,err));
		}
		return result;
	}
	 */
	/*
	static Vector parseArguments(String[] argv,PrintStream out,PrintStream err) throws
	FileNotFoundException, IOException, UnknownHostException
	{
		Vector result=null;
		int argBase=0;
		String src=null;
		if(argv.length>1) {
			if(argv.length>3) {
				argBase=1;
				src=argv[0];
			}
			result=new Vector();
			result.addElement(addProxy(src,argv[argBase++],argv[argBase++],argv[argBase++],out,err));
		} else if(argv.length==1) {
			result=parseConfigFile(argv[0],out,err);
		} else {
			throw new IllegalArgumentException(argsMessage);
		}
		return result;
	}
	 */
	/*
	public static void main(String[] argv) throws Exception {
		System.out.println("Java Transparent Proxy");
		System.out.println("Copyright (C) 1999 by Didier Frick (http://www.dfr.ch/)");
		System.out.println("This software is provided under the GNU general public license"+
				" (http://www.gnu.org/copyleft/gpl.html)");
		try {
			parseArguments(argv,System.out,System.err);
		} catch(IllegalArgumentException xc) {
			System.err.println(xc.getMessage());
			System.exit(1);
		}
	}
	 */
}
